package com.zhima.service;

import cn.hutool.core.convert.ConvertException;
import cn.hutool.json.JSONUtil;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.zhima.dto.AuthParamsDTO;
import com.zhima.dto.AuthType;
import com.zhima.dto.UserDTOExtension;
import com.zhima.exception.ZhimaOjException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import static com.zhima.exception.ErrorCode.AUTH_PARAM_NOT_VALID;

@Slf4j
@Service
public class OjUserDetailServiceImpl implements UserDetailsService, ApplicationContextAware {

    private ApplicationContext applicationContext;

    /**
     * 按照原本的框架，这里应该传入的是用户名username
     * 但是这里我们做了定制前端的传入的参数仍然是username
     * 但是这个值的含义变为了AuthParamsDto的JSON字符串
     * 实现统一登录入口
     *
     * @param jsonStr
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String jsonStr) throws UsernameNotFoundException {
        AuthParamsDTO authParamsDTO;
        try {
            authParamsDTO = JSONUtil.toBean(jsonStr, AuthParamsDTO.class);
        } catch (ConvertException e) {
            throw new ZhimaOjException(e, AUTH_PARAM_NOT_VALID);
        }
        AuthType authTypeEnum = authParamsDTO.getAuthType();
        if (!authTypeEnum.validate(authParamsDTO)) {
            throw new ZhimaOjException(AUTH_PARAM_NOT_VALID);
        }
        return buildUserDetails(
                applicationContext.getBean(String.format("%sAuthServiceImpl", authTypeEnum.getValue()), AuthService.class)
                        .auth(authParamsDTO)
        );
    }

    private UserDetails buildUserDetails(UserDTOExtension userDTOExtension) {
        // 避免密码泄露
        userDTOExtension.setPassword(null);
        return User
                .withUsername(JacksonUtils.toJson(userDTOExtension))
                // 因为我们定制SpringSecurity的校验逻辑
                // 不需要密码，这里传入框架的密码可以随便写
                .password("not_need_password")
                .authorities(userDTOExtension.getResources().toArray(new String[0]))
                .build();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
